package com.yinsin.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.math.BigInteger;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;

import com.yinsin.other.LogHelper;

/**
 * 文件操作工具类，提供一些常用的文件操作函数:
 * <ul>
 * <li>创建文件夹,判断目录是否存在，不存在则创建</li>
 * <li>从文件全名中获取后缀名 ,并转换成小写</li>
 * <li>从文件全名中获取后缀名 (带点),并转换成小写</li>
 * <li>读取文件内容返回字符串</li>
 * <li>读取文件内容返回字节数组</li>
 * <li>将内容写入文件（覆盖原始内容）</li>
 * <li>将内容写入文件（追加内容）</li>
 * <li>复制文件/文件夹线程</li>
 * <li>复制文件/文件线程</li>
 * <li>删除文件线程</li>
 * <li>复制文件夹</li>
 * <li>复制文件</li>
 * <li>获得指定目录的父目录</li>
 * <li>由文件字节数计算文件大小，单位Kb，Mb，Gb</li>
 * <li>数值四舍五入，最多保留两位小数</li>
 * <li>数值四舍五入，自定义保留小数位位数</li>
 * <li>根据路径获取文件名称</li>
 * <li>判断文件类型，返回相应的目录名称</li>
 * <li>给文件名称加上指定的后缀（在点号之前比如：music.mp3 -&gt; music_01.mp3）</li>
 * <li>获取文件MD5码</li>
 * </ul>
 * 
 * @author Yisin
 * @version 1.0
 */
public class FileUtils {
    private static final LogHelper logger = LogHelper.getLogger(FileUtils.class);

    /**
     * 创建文件夹,判断目录是否存在，不存在则创建
     * 
     * @param dirPath
     *            文件夹完整路径
     * @return boolean true:创建目录成功 false:创建目录失败
     */
    public static boolean createDir(String dirPath) {
        boolean fileExists = false;
        // 判断文件路径是否为空
        if (CommonUtils.isNotBlank(dirPath)) {
            File file = new File(dirPath);
            // 判断文件不存在
            if (!(fileExists = file.exists())) {
                // 创建目录
                fileExists = file.mkdirs();
            }
        }
        return fileExists;
    }
    
    /**
     * 创建文件夹, 判断目录是否存在，不存在则创建
     * 
     * @param dirPath
     *            文件夹完整路径
     * @param override
     *            是否覆盖，true不管是否存在都创建
     * @return boolean true:创建目录成功 false:创建目录失败
     */
    public static boolean createDir(String dirPath, boolean override) {
        boolean fileExists = false;
        // 判断文件路径是否为空
        if (CommonUtils.isNotBlank(dirPath)) {
            File file = new File(dirPath);
            // 判断文件不存在
            if (!(fileExists = file.exists()) || override) {
                // 创建目录
                fileExists = file.mkdirs();
            }
        }
        return fileExists;
    }

    /**
     * 创建文件,判断文件是否存在，不存在则创建
     * 
     * @param filepath
     *            文件完整路径
     * @return boolean true:创建成功 false:创建失败
     * @throws IOException
     */
    public static boolean createFile(String filepath) throws IOException {
        boolean fileExists = false;
        // 判断文件路径是否为空
        if (CommonUtils.isNotBlank(filepath)) {
            File file = new File(filepath);
            // 判断文件不存在
            if (!(fileExists = file.exists())) {
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                // 创建文件
                fileExists = file.createNewFile();
            }
        }
        return fileExists;
    }

    /**
     * 从文件全名中获取后缀名 ,并转换成小写
     * 
     * @param fileName
     *            获取文件后缀名
     * @return String 后缀名成
     */
    public static String getFileSuffix(String fileName) {
        String prefix = null;
        // 判断文件名称不为空
        if (CommonUtils.isNotBlank(fileName)) {
            // 判断文件名称是否包含字符.
            if (fileName.lastIndexOf(".") != -1) {
                // 获取文件后缀
                prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
            }
        }
        return CommonUtils.isBlank(prefix) ? Constants.CHAR_EMPTY : prefix.toLowerCase();
    }

    /**
     * 从文件全名中获取后缀名 (带点),并转换成小写
     * 
     * @param fileName
     *            获取文件后缀名
     * @return String 后缀名成
     */
    public static String getFilePointSuffix(String fileName) {
        String prefix = null;
        // 判断文件名称不为空
        if (CommonUtils.isNotBlank(fileName)) {
            // 判断文件名称是否包含字符.
            if (fileName.lastIndexOf(".") != -1) {
                // 获取文件后缀
                prefix = fileName.substring(fileName.lastIndexOf("."));
            }
        }
        return CommonUtils.isBlank(prefix) ? Constants.CHAR_EMPTY : prefix.toLowerCase();
    }
    
    /**
     * 从文件全名中获取文件名称（不带后缀）
     * @param fileName
     * @return
     */
    public static String getFileNameNotSuffix(String fileName){
    	String prefix = fileName;
    	String suffix = getFilePointSuffix(fileName);
    	if (CommonUtils.isNotBlank(suffix)) {
    		prefix = fileName.substring(0, fileName.length() - suffix.length());
    	}
    	return prefix;
    }
    
    /**
     * 替换文件名称（不包含后缀）
     * @param fileName 文件名称（包含后缀）
     * @param newNameNotSuffix 新名称（不包含后缀）
     * @return
     */
    public static String replaceFileName(String fileName, String newNameNotSuffix){
    	String suffix = getFilePointSuffix(fileName);
    	if (CommonUtils.isNotBlank(suffix)) {
    		return newNameNotSuffix + suffix;
    	}
    	return fileName;
    }

    /**
     * 读取文件内容返回字符串
     * 
     * @param file File            
     * @return String
     * @throws IOException
     */
    public static String readFileAsString(File file) throws IOException {
        String result = null;
        if (file.exists()) {
            FileInputStream fis = new FileInputStream(file);
            byte[] byt = new byte[1024];
            int index = -1;
            StringBuffer sb = new StringBuffer();
            while ((index = fis.read(byt)) != -1) {
                sb.append(new String(byt, 0, index));
            }
            fis.close();
            result = sb.toString();
        } else {
            throw new FileNotFoundException("Not Find File：" + file.getAbsolutePath());
        }
        return result;
    }

    /**
     * 读取文件内容返回字节数组
     * 
     * @param file
     * @return byte[]
     * @throws IOException
     */
    public static byte[] readFileAsByte(File file) throws IOException {
        byte[] byts = null;
        if (file.exists()) {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] byt = new byte[1024];
            while (fis.read(byt) != -1) {
                baos.write(byt);
            }
            fis.close();
            byts = baos.toByteArray();
        } else {
            throw new FileNotFoundException("Not Find File：" + file.getAbsolutePath());
        }
        return byts;
    }

    /**
     * 将内容写入文件（覆盖原始内容）
     * 
     * @param filePath String            
     * @param content String
     *            
     * @throws FileNotFoundException
     */
    public static void writeFileAsCover(String filePath, String content) throws FileNotFoundException {
        writeFileAsCover(filePath, content, null);
    }
    
    /**
     * 将内容写入文件（覆盖原始内容）
     * 
     * @param filePath String            
     * @param content String            
     * @param charset String
     *            
     * @throws FileNotFoundException
     */
    public static void writeFileAsCover(String filePath, String content, String charset) throws FileNotFoundException {
        File file = new File(filePath);
        if (file.exists()) {
            FileOutputStream fos = new FileOutputStream(file);
            try {
            	if(CommonUtils.isNotBlank(charset)){
            		fos.write(content.getBytes(charset));
            	} else {
            		fos.write(content.getBytes());
            	}
                fos.flush();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            throw new FileNotFoundException("Not Find File：" + file.getAbsolutePath());
        }
    }
    
    /**
     * 将内容写入文件（追加内容）
     * 
     * @param filePath String            
     * @param content String
     *            
     * @throws FileNotFoundException
     */
    public static void writeFileAsAppend(String filePath, String content) throws FileNotFoundException, IOException {
    	writeFileAsAppend(filePath, content);
    }
    
    /**
     * 将内容写入文件（追加内容）
     * 
     * @param filePath String     *            
     * @param content String           
     * @param charset String
     *            
     * @throws FileNotFoundException
     */
    public static void writeFileAsAppend(String filePath, String content, String charset) throws FileNotFoundException, IOException {
        File file = new File(filePath);
        if (file.exists()) {
        	 RandomAccessFile randomFile = null;  
             try {     
                 // 打开一个随机访问文件流，按读写方式     
                 randomFile = new RandomAccessFile(filePath, "rw");     
                 // 文件长度，字节数     
                 long fileLength = randomFile.length();     
                 // 将写文件指针移到文件尾。     
                 randomFile.seek(fileLength);
                 if(CommonUtils.isNotBlank(charset)){
                	 randomFile.write(content.getBytes(charset));      
                 } else {
                	 randomFile.writeBytes(content);      
                 }
             } catch (IOException e) {     
            	 throw e;
             } finally{  
                 if(randomFile != null){  
                     try {  
                         randomFile.close();  
                     } catch (IOException e) {  
                    	 throw e;  
                     }
                 }  
             }  
        } else {
            throw new FileNotFoundException("Not Find File：" + file.getAbsolutePath());
        }
    }

    /**
     * 文件类型枚举<br>
     * FILE 文件<br>
     * DIR 目录
     * 
     * @author Yisin
     *
     */
    public static enum FILE_TYPE {
        /** 文件 */
        FILE,
        /** 文件夹（目录） */
        DIR
    }

    /**
     * 复制文件/文件夹线程
     * 
     * @param source String         
     * @param target String
     *            
     * @author yisin
     */
    public static void copyDirThread(String source, String target) {
        new FileThread(source, target, FILE_TYPE.DIR).start();
    }

    /**
     * 复制文件/文件线程
     * 
     * @param source String
     * @param target String
     * @author yisin
     */
    public static void copyFileThread(String source, String target) {
        new FileThread(source, target, FILE_TYPE.FILE).start();
    }

    /**
     * 删除文件线程
     * 
     * @author yisin
     * @param filePath
     */
    public static void deleteFileThread(String filePath) {
        new DeleteFileThread(filePath).start();
    }

    /**
     * 复制文件夹
     * 
     * @author yisin
     * @param source
     *            源目录路径
     * @param target
     *            目标目录路径
     */
    public static void copyDir(String source, String target) {
        if (source != null && target != null && !target.equalsIgnoreCase(source)) {
            File sourceFiles = new File(source);
            File targetFiles = new File(target);
            if (!targetFiles.exists()) {
                targetFiles.mkdirs();
            }
            File tarFile = null;
            File[] files = sourceFiles.listFiles();
            for (File file : files) {
                if (file.isFile()) {
                    tarFile = new File(targetFiles.getAbsolutePath() + File.separator + file.getName());
                    copyFile(file, tarFile);
                } else {
                    copyDir(sourceFiles.getAbsolutePath() + File.separator + file.getName(), targetFiles.getAbsolutePath() + File.separator + file.getName());
                }
            }
        }
    }

    /**
     * 复制文件
     * 
     * @author yisin
     * @param source 源文件
     * @param target 目标文件
     */
    public static void copyFile(File source, File target) {
        if (!target.isFile() && !target.exists()) {
            try {
                byte[] by = new byte[1024];
                FileInputStream in = new FileInputStream(source);
                FileOutputStream out = new FileOutputStream(target);
                while (in.read(by) > 0) {
                    out.write(by);
                }
                in.close();
                out.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获得指定目录的父目录
     * 
     * @param dirPath
     *            当前路径
     * @return 上一级目录路径
     */
    public static String getParentDir(String dirPath) {
        String parentDir = null;
        if (CommonUtils.isNotBlank(dirPath)) {
            if (dirPath.endsWith("/")) {
                dirPath = dirPath.substring(0, dirPath.length() - 1);
            }
            if (dirPath.lastIndexOf("/") != -1) {
                parentDir = dirPath.substring(0, dirPath.lastIndexOf("/"));
            }
        }
        return parentDir;
    }

    /**
     * 由文件字节数计算文件大小，单位Kb，Mb，Gb
     * 
     * @param size
     * @return
     */
    public static String getFileSize(long size) {
        float fa = size;
        String kb = "0 Kb";
        if (size < (1024 * 1024)) {
            kb = CommonUtils.round(fa / 1024) + " Kb";
        } else if (size >= (1024 * 1024) && size < (1024 * 1024 * 1024)) {
            kb = CommonUtils.round(fa / (1024 * 1024)) + " Mb";
        } else {
            kb = CommonUtils.round(fa / (1024 * 1024 * 1024)) + " Gb";
        }
        return kb;
    }

    /**
     * 根据路径获取文件名称
     * 
     * @param filePath
     * @return
     */
    public static String getFileNameToPath(String filePath) {
        String fileName = Constants.CHAR_EMPTY;
        if (null != filePath) {
            int index = filePath.lastIndexOf("/");
            if (index != -1) {
                fileName = filePath.substring(index);
            } else {
                index = filePath.lastIndexOf("\\");
                if (index != -1) {
                    fileName = filePath.substring(index + 1);
                } else {
                    index = filePath.lastIndexOf("\\\\");
                    if (index != -1) {
                        fileName = filePath.substring(index + 2);
                    }
                }
            }
        }
        return fileName;
    }

    /**
     * 判断文件类型，返回相应的目录名称
     * 
     * @param fileName
     * @return
     */
    public static String getFileDirName(String fileName) {
        String dirs = "other";
        if (null != fileName) {
            String fix = getFilePointSuffix(fileName);
            if (null != fix && !Constants.CHAR_EMPTY.trim().equals(fix)) {
                if (fix.toLowerCase().equals(".mp3")) {
                    dirs = "media";
                } else if (fix.toLowerCase().equals(".wma")) {
                    dirs = "media";
                } else if (fix.toLowerCase().equals(".wmv")) {
                    dirs = "media";
                } else if (fix.toLowerCase().equals(".avi")) {
                    dirs = "media";
                } else if (fix.toLowerCase().equals(".flv")) {
                    dirs = "media";
                } else if (fix.toLowerCase().equals(".swf")) {
                    dirs = "media";
                } else if (fix.toLowerCase().equals(".rmvb")) {
                    dirs = "media";
                } else if (fix.toLowerCase().equals(".jpg")) {
                    dirs = "image";
                } else if (fix.toLowerCase().equals(".gif")) {
                    dirs = "image";
                } else if (fix.toLowerCase().equals(".png")) {
                    dirs = "image";
                } else if (fix.toLowerCase().equals(".bmp")) {
                    dirs = "image";
                } else if (fix.toLowerCase().equals(".ico")) {
                    dirs = "image";
                } else if (fix.toLowerCase().equals(".txt")) {
                    dirs = "text";
                } else if (fix.toLowerCase().equals(".html")) {
                    dirs = "text";
                } else if (fix.toLowerCase().equals(".htm")) {
                    dirs = "text";
                } else if (fix.toLowerCase().equals(".jsp")) {
                    dirs = "text";
                } else if (fix.toLowerCase().equals(".asp")) {
                    dirs = "text";
                } else if (fix.toLowerCase().equals(".xml")) {
                    dirs = "text";
                } else if (fix.toLowerCase().equals(".rar")) {
                    dirs = "application";
                } else if (fix.toLowerCase().equals(".zip")) {
                    dirs = "application";
                } else if (fix.toLowerCase().equals(".exe")) {
                    dirs = "application";
                } else if (fix.toLowerCase().equals(".iso")) {
                    dirs = "application";
                } else if (fix.toLowerCase().equals(".7z")) {
                    dirs = "application";
                } else {
                    dirs = "archive";
                }
            }
        }
        return dirs;
    }

    /**
     * 给文件名称加上指定的后缀（在点号之前比如：music.mp3 -&gt; music_01.mp3）
     * 
     * @param fileName
     * @param fix
     * @return
     */
    public static String addFileNameSuffix(String fileName, String fix) {
        if (fileName != null && fileName.indexOf(".") != -1) {
            int index = fileName.lastIndexOf(".");
            fileName = fileName.substring(0, index) + fix + fileName.substring(index);
        }
        return fileName;
    }

    /**
     * 获取文件MD5码
     * @param file
     * @return
     */
    public static String getMd5ByFile(File file) {
        String value = null;
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(byteBuffer);
            BigInteger bi = new BigInteger(1, md5.digest());
            value = bi.toString(16);
        } catch (Exception e) {
            logger.error("获取文件MD5码异常：" + e.getMessage());
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return value;
    }

    /**
     * 文件复制线程类
     * 
     * @author Yisin
     */
    private static class FileThread extends Thread {
        private String source;
        private String target;
        private FILE_TYPE type = FILE_TYPE.FILE;

        public FileThread(String source, String target, FILE_TYPE type) {
            this.source = source;
            this.target = target;
            this.type = type;
        }

        @Override
        public void run() {
            if (type == FILE_TYPE.DIR) {
                FileUtils.copyDir(source, target);
            } else if (type == FILE_TYPE.FILE) {
                FileUtils.copyFile(new File(source), new File(target));
            }
        }
    }

    /**
     * 文件删除线程类
     * 
     * @author Yisin
     */
    private static class DeleteFileThread extends Thread {
        private String filePath;
        private File file;
        private int time = 0;

        public DeleteFileThread(String filePath) {
            this.filePath = filePath;
            file = new File(filePath);
        }

        @Override
        public void run() {
            try {
                while (true) {
                    if (time < 3) {
                        Thread.sleep(1000);
                        time++;
                    } else {
                        if (file != null && file.exists()) {
                            logger.info("被删除文件：" + file.getName());
                            logger.info("\t路径：" + filePath);
                            file.delete();
                        }
                        return;
                    }
                }
            } catch (InterruptedException e) {
            }
        }
    }
    
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        File file = new File("D:\\application\\开发相关\\QIYImedia_0_27.exe");
        String md5 = getMd5ByFile(file);
        long end = System.currentTimeMillis();
        System.out.println(file.getTotalSpace() + ", " + md5 + "， 耗时：" + (end - start));
        System.out.println(getFilePointSuffix("QIYImedia_0_27.exe"));
    }

}
